/*
Acess OS
Proc/Sys Filesystem
*/
// === INCLUDES ===
#include <acess.h>
#include <vfs.h>
#include <fs_devfs2.h>

// === CONSTANT ===
#define ENTS_PER_BLOCK	42

// === PROTOTYPES ===
 int	ProcDev_Install();
vfs_node	*ProcDev_Read_LogType(vfs_node *node, int dirPos);
 int	ProcDev_Read(vfs_node *node, int offset, int length, void *buffer);
 int	ProcDev_Write(vfs_node *node, int offset, int length, void *buffer);
 int	ProcDev_MkNod(vfs_node *node, char *path, int flags);
vfs_node	*ProcDev_ReadDir(vfs_node *node, int dirPos);
vfs_node	*ProcDev_FindDir(vfs_node *node, char *name);

// === TYPES ===
typedef struct sLogEntry {
	Uint64	Timestamp;
	Uint32	Module;
	Uint	Type;	//0:Null,1:Warning,2:Notice,3:Debug
	 int	Length;
	char	*Data;
} tLogEntry;	// Sizeof = 24 bytes
typedef struct sLogBlock {
	tLogEntry	Ents[ENTS_PER_BLOCK];
	struct sLogBlock	*Next;
} tLogBlock;	// Sizeof = 1008

// === SEMI-GLOBALS ===
 int	gbLogSpinlock = 0;
tLogBlock	*gProcDev_Log = NULL;
tLogEntry	*gProcDev_LogLast = NULL;
vfs_node	gProcDev_LogClasses[] = {
	{.inode=1,.name="warning",.nameLength=7,.mode=0111,.flags = VFS_FFLAG_DIRECTORY,.readdir=ProcDev_Read_LogType,.finddir=ProcDev_FindDir},
	{.inode=2,.name="notice",.nameLength=6,.mode=0111,.flags = VFS_FFLAG_DIRECTORY,.readdir=ProcDev_Read_LogType,.finddir=ProcDev_FindDir},
	{.inode=3,.name="debug",.nameLength=5,.mode=0111,.flags = VFS_FFLAG_DIRECTORY,.readdir=ProcDev_Read_LogType,.finddir=ProcDev_FindDir}
};
vfs_node	gProcDev_RootItems[] = {
	{.name="logs",.nameLength=4,.mode=0111,.length=SIZEOF_ARR(gProcDev_LogClasses),.flags = VFS_FFLAG_DIRECTORY,.readdir=ProcDev_ReadDir,.finddir=ProcDev_FindDir,.ImplPtr=gProcDev_LogClasses}
};
devfs_driver	gProcDev_Driver = {
	{
	.name = "proc",
	.nameLength = 4,
	.length = SIZEOF_ARR(gProcDev_RootItems),
	.flags = VFS_FFLAG_DIRECTORY,
	.mode = 0755,	// RWXR-XR-X
	.uid = 0,	// Owner: Root
	.gid = 0,	// Group: Root
	.readdir = ProcDev_ReadDir,
	.finddir = ProcDev_FindDir,
	.ImplPtr = gProcDev_RootItems	// Allow Generalised ReadDir
	}, NULL
};
// Register with module loader
//DEFMOD(Proc, ProcDev_Install);

// === CODE ===
/**
 * \fn int ProcDev_Install()
 * \brief Install the proc filesystem
 */
int ProcDev_Install()
{
	// Register
	gProcDev_Driver.rootNode.impl = dev_addDevice( &gProcDev_Driver );
	return 1;
}

/**
 * \fn int ProcDev_Read_Log(vfs_node *node, Uint64 offset, Uint64 length, void *buffer)
 * \brief Read from a log
 */
int ProcDev_Read_Log(vfs_node *node, Uint64 offset, Uint64 length, void *buffer)
{
	tLogEntry	*ent = (tLogEntry *) node->ImplPtr;

	
	// Bound Check
	if(offset >= ent->Length) {
		return 0;
	}
	
	if(offset + length > ent->Length)
		length = ent->Length - offset;
	memcpy(buffer, ent->Data+offset, length);
	return length;
}

/**
 * \fn vfs_node *ProcDev_Read_LogType(vfs_node *node, int dirPos)
 * \brief Read from a log type directory
 * \param node	Parent Node
 * \param dirPos	Directory Position
 * \return Child node or NULL past last entry
 */
vfs_node *ProcDev_Read_LogType(vfs_node *node, int dirPos)
{
	 int	iBlock = dirPos / ENTS_PER_BLOCK;
	// int	iOfs = dirPos % ENTS_PER_BLOCK;
	tLogBlock	*pBlock = gProcDev_Log;
	
	while(pBlock && iBlock--)	pBlock = pBlock->Next;
	if(!pBlock)	return NULL;
	
	
	
	return NULL;
}

/**
 \fn int ProcDev_ReadDir(vfs_node *node, int dirPos)
 \brief Read from an array stored in `node->link`
 \param node	Parent node
 \param dirPos	Directory position
 \return Child node or NULL on error / last entry
 \note Also returns &NULLNode on a blank, non-last entry
*/
vfs_node *ProcDev_ReadDir(vfs_node *node, int dirPos)
{
	if(dirPos >= node->length)	return NULL;
	return &((vfs_node*)node->ImplPtr)[dirPos];
}

/**
 \fn int ProcDev_FindDir(vfs_node *node, char *name)
 \param node	Parent node
 \param name	Name of file to locate
 \return Child node or NULL
 Calls ReadDir repeatedly to find the file
*/
vfs_node *ProcDev_FindDir(vfs_node *parent, char *name)
{
	 int	i = 0;
	vfs_node	*node;
	for( ; i < parent->length; i++ )
	{
		node = parent->readdir(parent, i);
		if(strcmp(name, node->name) == 0)	return node;
	}
	return NULL;
}

// ---
// Kernel Interface Functions
// ---
/**
 * \fn void ProcDev_Log_Warning(char *SubSys, char *msg, void *args)
 * \brief Log a warning
 */
void ProcDev_Log_Warning(char *SubSys, char *msg, void *args)
{
	char	buf[1024];
	tLogEntry	*pLogEnt;
	 int	len;
	len = sprintfv(buf, msg, args);
	
	pLogEnt = malloc(sizeof(tLogEntry) + len + 1);
	pLogEnt->Timestamp = unow();
	pLogEnt->Module = SubSys ? *(Uint32*)SubSys : 0;
	pLogEnt->Type = 1;	// Warning
	memcpy(pLogEnt->Data, buf, len+1);
	
	/*
	ACQ_LOCK( &gbLogSpinlock );
	pLogEnt->Next = gProcDev_Log;
	gProcDev_Log = pLogEnt;
	REL_LOCK( &gbLogSpinlock );
	*/
}
